package com.tannuo.dolphinnotedemo.sdk.device.protocol;

import com.tannuo.dolphinnotedemo.sdk.util.DataLog;
import com.tannuo.dolphinnotedemo.sdk.util.DataUtil;
import com.tannuo.dolphinnotedemo.sdk.util.Logger;

import java.nio.ByteBuffer;

/**
 * Created by hucn on 2016/7/28.
 * Description: 自定义的协议
 */
public class ZDProtocol extends ProtocolBase {

    private static final String TAG = ZDProtocol.class.getSimpleName();

    // 不带参数的消息类型
    private static final byte FEATURE_REQUEST_SNAPSHOT = (byte) 0xA0; // 由白板->手机，提示手机抓屏
    private static final byte FEATURE_REQUEST_TO_DISCONNECT = (byte) 0xA1; // 白板<->手机，手机和白板连接相互结束，白板处待连状态
    private static final byte FEATURE_REQUEST_TO_REPEAT = (byte) 0xA2; // 白板<->手机，请求对方再次发送上次的数据包
    private static final byte FEATURE_RESERVE_01 = (byte) 0xA3; // 保留字段1
    private static final byte FEATURE_RESERVE_02 = (byte) 0xA4; // 保留字段2
    private static final byte FEATURE_RESERVE_03 = (byte) 0xA5; // 保留字段3
    private static final byte FEATURE_RESPONSE_RECEIVED = (byte) 0xA6; // 白板<->手机，除指令重发,应答及信息请求,连接请求,错误通告信息等自有应答或无需应答指令外,收到其它正常指令,要给出应答信号
    private static final byte FEATURE_REQUEST_HARDWARE = (byte) 0xA7; // 白板->手机, 请求白板发送现有的固件版本号等信息（无需手机发送正常应答0xA6）
    private static final byte FEATURE_REQUEST_CURRENT_RECORD_STATUS = (byte) 0xA8; // 请求当前录制状态信息
    private static final byte FEATURE_BEAT = (byte) 0xA9; // 手机心跳指令、

    // 带参数的消息类型
    private static final int FEATURE_REQUEST_SNAPSHOT_INFO = (byte) 0x70; // 提示手机抓屏,测试使用
    private static final byte FEATURE_REQUEST_START_RECORDING = (byte) 0x71; // 参数为四个字节：unsigned int nms，指示手机录像带时间参数，由白板->手机
    private static final byte FEATURE_REQUEST_STOP_RECORDING = (byte) 0x72; // 参数为四个字节：unsigned int nms，指示手机录像结束带时间参数，由白板->手机
    private static final byte FEATURE_REQUEST_TIME_REVISION = (byte) 0x73; // 参数为四个字节：unsigned int nms，手机向白板要求对时，由手机->白板 ，（无需白板发送正常应答0xA6）
    private static final byte FEATURE_RESPONSE_TIME_REVISION = (byte) 0x74; // 参数为四个字节：unsigned int nms，手机向白板要求对时，由手机->白板 ，（无需白板发送正常应答0xA6）
    private static final byte FEATURE_REQUEST_CONNECTING = (byte) 0x75; // 参数为四个字节：unsigned int nms，手机向白板要求对时，由手机->白板 ，（无需白板发送正常应答0xA6）
    private static final byte FEATURE_RESPONSE_CONNECTING_SUCCESS = (byte) 0x76; // 连接应答成功
    private static final byte FEATURE_RESPONSE_CONNECTING_ERROR = (byte) 0x77; // 连接应答错误
    private static final byte FEATURE_RESPONSE_ERROR = (byte) 0x78; // 错误信息返回
    private static final byte FEATURE_REQUEST_INFO_CONTROL = (byte) 0x79; // 提示信息控制
    private static final byte FEATURE_RESPONSE_CURRENT_RECORD_STATUS = (byte) 0xB0; // 返回当前的录制状态
    private static final byte FEATURE_RESPONSE_HARDWARE = (byte) 0xB1; // 返回硬件信息
    private static final byte FEATURE_RESPONSE_SCREEN = (byte) 0xB2; // 返回屏幕信息
    private static final byte FEATURE_REQUEST_SET_QR_CODE = (byte) 0x5A; // 设置二维码信息
    private static final byte FEATURE_REQUEST_SET_SCREEN = (byte) 0x5B; // 设置触屏信息
    private static final byte FEATURE_REQUEST_SET_THRESHOLD = (byte) 0x5C; // 设置触屏点变化的有效阈值
    private static final byte FEATURE_REQUEST_SET_INTERVAL = (byte) 0x5D; // 设置点的发送时间间隔
    private static final byte FEATURE_POINT_DATA = (byte) 0xDD; // 接收到点的数据

    // 消息的格式
    private static final byte[] MESSAGE_HEADER = {(byte) 0xFA, (byte) 0x0A};
    private static final byte[] MESSAGE_END = {(byte) 0xED};

    // 点的落笔，滑动、抬起的标志位
    public static final byte ACTION_DOWN = (byte) 0x03; // 落下
    public static final byte ACTION_MOVE = (byte) 0x00; // 移动
    public static final byte ACTION_UP = (byte) 0x0C; // 抬起

    // 定义消息各个部分的长度
    private static final int headerLength = 2; // 头
    private static final int featureLength = 1; // 命令
    private static final int idLength = 2; // id
    private static final int lengthLength = 2; // 长度
    private static final int checkDigitLength = 1; // 校验位
    private static final int endLength = 1; // 尾

    @Override
    public IMessage decode(byte[] data) {
        if (null == data || data.length == 0) {
            throw new IllegalArgumentException();
        }

        IMessage parsed = new ZDMessage();

        DataLog.getInstance().writeInData(data);
        DataLog.getInstance().writeInLineData(data);
        this.reset();

        if (lastUnhandledBytes.length != 0) {
            lastUnhandledBytes = new byte[0];
        }

        ByteBuffer msgBuffer = ByteBuffer.wrap(data);

        byte[] header = new byte[headerLength];
        msgBuffer.get(header, 0, header.length);
        if (header[0] != MESSAGE_HEADER[0] || header[1] == MESSAGE_HEADER[1]) {
            parsed.setFeature(ERROR_HEADER);
            return parsed;
        }

        byte[] feature = new byte[featureLength];
        msgBuffer.get(feature, 0, feature.length);
        if (feature[0] == (byte) 0x00) {
            parsed.setFeature(ERROR_DATA_FEATURE);
            return parsed;
        }

        byte[] id = new byte[idLength];
        msgBuffer.get(id, 0, id.length);
        if (id[0] == (byte) 0x00 && id[1] == (byte) 0x00) {
            parsed.setFeature(ERROR_ID);
            return parsed;
        }

        byte[] length = new byte[lengthLength];
        msgBuffer.get(length, 0, length.length);
        int dataLength = DataUtil.bytesToIntLittleEndian(length);

        boolean checkLengthResult = checkLength(length, data);
        if (!checkLengthResult) {
            parsed.setFeature(ERROR_DATA_LENGTH);
            return parsed;
        }

        byte[] content = new byte[dataLength];
        msgBuffer.get(content, 0, content.length);

        byte[] checkDigit = new byte[checkDigitLength];
        msgBuffer.get(checkDigit, 0, checkDigit.length);
        if (!checkDigit(checkDigit, data)) {
            parsed.setFeature(ERROR_DATA);
            return parsed;
        }

        byte[] end = new byte[endLength];
        msgBuffer.get(end, 0, end.length);
        if (end[0] != MESSAGE_END[0]) {
            parsed.setFeature(ERROR_END);
            return parsed;
        }

        boolean checkSumResult = checkSum(data, header, feature, id, length, content, checkDigit, end);
        if (!checkSumResult) {
            Logger.e(TAG, "CheckSum error when parsed.");
            parsed.setFeature(ERROR_CHECKSUM);
            return parsed;
        }

        int result = getFeatureResult(feature);
        parsed.setFeature(result);
        parsed.setId(id);
        if (parsed.getFeature() != FEATURE_POINT_DATA) {
            parsed.setCommondContent(content);
        } else {
            parsed.setPointsContent(content);
        }
        Logger.d(TAG, "Parsed byte[] data successfully, return IMessage object to the protocolHandler.");

        return parsed;
    }

    @Override
    public byte[] encode(IMessage message) {
        if (message == null) {
            throw new IllegalArgumentException();
        }
        if (message.getFeature() == 0
                || message.getid() == null
                || message.getCommondContent() == null
                || (message.getCommondContent() == null && message.getPointsContent() == null)) {
            throw new IllegalArgumentException();
        }
        ByteBuffer messageBuffer = ByteBuffer.allocate(getTotalLength(message));
        messageBuffer.put(MESSAGE_HEADER, 0, headerLength);
        messageBuffer.put(DataUtil.int2byteLen2Low(message.getFeature()), 0, featureLength);
        messageBuffer.put(message.getid(), 0, idLength);
        messageBuffer.put(DataUtil.int2byteLen2Low(getContentLength(message)), 0, lengthLength);
        byte[] content = getContent(message);
        messageBuffer.put(content, 0, content.length);
        byte[] checkDigit = getcheckDigit(message);
        messageBuffer.put(checkDigit, 0, checkDigitLength);
        messageBuffer.put(MESSAGE_END, 0, endLength);
        return messageBuffer.array();
    }

    private boolean checkLength(byte[] length, byte[] data) {
        //// TODO: 2016/7/28 to do the check
        return true;
    }

    private boolean checkSum(byte[] data, byte[] header, byte[] feature, byte[] id, byte[] length, byte[] content, byte[] checkDigit, byte[] end) {
        int totalMessageLength = data.length;
        int countLength = header.length + feature.length + id.length + length.length + content.length + checkDigit.length + end.length;
        return totalMessageLength == countLength;
    }

    private boolean checkDigit(byte[] checkDigit, byte[] data) {
        //// TODO: 2016/7/28 to do the check
        return true;
    }

    private byte[] getcheckDigit(IMessage message) {
        return new byte[]{(byte) 0x00};
    }

    /**
     * 进行编码时获得数据总的长度
     */
    public int getTotalLength(IMessage message) {
        //根据数据域的长度大小赋值
        int totalLength = 0;
        if (message == null) {
            return totalLength;
        }
        int dataLength = getContentLength(message);
        totalLength = dataLength + headerLength + featureLength + idLength + lengthLength + checkDigitLength + endLength;
        return totalLength;
    }

    /**
     * 获取消息内容的长度
     */
    public int getContentLength(IMessage message) {
        int dataLength = 0;
        if (message == null) {
            return dataLength;
        }
        if (message.getFeature() == FEATURE_POINT_DATA) {
            dataLength = message.getPointsContent().length;
        } else {
            dataLength = message.getCommondContent().length;
        }
        return dataLength;
    }

    /**
     * 获取消息的内容
     */
    public byte[] getContent(IMessage message) {
        byte[] content;
        if (message.getFeature() == FEATURE_POINT_DATA) {
            content = message.getPointsContent();
        } else {
            content = message.getCommondContent();
        }
        return content;
    }

    /**
     * 根据feature获得相应的指令
     */
    private int getFeatureResult(byte[] feature) {
        int fratureInt = ERROR_NONE;
        switch (feature[0]) {
            case FEATURE_REQUEST_SNAPSHOT:
                fratureInt = ProtocolBase.STATUS_REQUEST_SNAPSHOT;
                break;
            case FEATURE_REQUEST_TO_DISCONNECT:
                fratureInt = ProtocolBase.STATUS_REQUEST_TO_DISCONNECT;
                break;
            case FEATURE_REQUEST_TO_REPEAT:
                fratureInt = ProtocolBase.STATUS_REQUEST_TO_REPEAT;
                break;
            case FEATURE_RESERVE_01:

                break;
            case FEATURE_RESERVE_02:

                break;
            case FEATURE_RESERVE_03:

                break;
            case FEATURE_RESPONSE_RECEIVED:
                fratureInt = ProtocolBase.STATUS_RESPONSE_RECEIVED;
                break;
            case FEATURE_REQUEST_HARDWARE:
                fratureInt = ProtocolBase.STATUS_REQUEST_HARDWARE;
                break;
            case FEATURE_REQUEST_CURRENT_RECORD_STATUS:
                fratureInt = ProtocolBase.STATUS_REQUEST_CURRENT_RECORD_STATUS;
                break;
            case FEATURE_BEAT:
                fratureInt = ProtocolBase.STATUS_BEAT;
                break;
            case FEATURE_REQUEST_SNAPSHOT_INFO:
                fratureInt = ProtocolBase.STATUS_REQUEST_SNAPSHOT_INFO;
                break;
            case FEATURE_REQUEST_START_RECORDING:
                fratureInt = ProtocolBase.STATUS_REQUEST_START_RECORDING;
                break;
            case FEATURE_REQUEST_STOP_RECORDING:
                fratureInt = ProtocolBase.STATUS_REQUEST_STOP_RECORDING;
                break;
            case FEATURE_REQUEST_TIME_REVISION:
                fratureInt = ProtocolBase.STATUS_REQUEST_TIME_REVISION;
                break;
            case FEATURE_RESPONSE_TIME_REVISION:
                fratureInt = ProtocolBase.STATUS_REQUEST_SNAPSHOT;
                break;
            case FEATURE_REQUEST_CONNECTING:
                fratureInt = ProtocolBase.STATUS_RESPONSE_TIME_REVISION;
                break;
            case FEATURE_RESPONSE_CONNECTING_SUCCESS:
                fratureInt = ProtocolBase.STATUS_RESPONSE_CONNECTING_SUCCESS;
                break;
            case FEATURE_RESPONSE_CONNECTING_ERROR:
                fratureInt = ProtocolBase.STATUS_RESPONSE_CONNECTING_ERROR;
                break;
            case FEATURE_RESPONSE_ERROR:
                fratureInt = ProtocolBase.STATUS_RESPONSE_ERROR;
                break;
            case FEATURE_REQUEST_INFO_CONTROL:
                fratureInt = ProtocolBase.STATUS_REQUEST_INFO_CONTROL;
                break;
            case FEATURE_RESPONSE_CURRENT_RECORD_STATUS:
                fratureInt = ProtocolBase.STATUS_RESPONSE_CURRENT_RECORD_STATUS;
                break;
            case FEATURE_RESPONSE_HARDWARE:
                fratureInt = ProtocolBase.STATUS_RESPONSE_HARDWARE;
                break;
            case FEATURE_RESPONSE_SCREEN:
                fratureInt = ProtocolBase.STATUS_RESPONSE_SCREEN;
                break;
            case FEATURE_REQUEST_SET_QR_CODE:
                fratureInt = ProtocolBase.STATUS_REQUEST_SET_QR_CODE;
                break;
            case FEATURE_REQUEST_SET_SCREEN:
                fratureInt = ProtocolBase.STATUS_REQUEST_SET_SCREEN;
                break;
            case FEATURE_REQUEST_SET_THRESHOLD:
                fratureInt = ProtocolBase.STATUS_REQUEST_SET_THRESHOLD;
                break;
            case FEATURE_REQUEST_SET_INTERVAL:
                fratureInt = ProtocolBase.STATUS_REQUEST_SET_INTERVAL;
                break;
            case FEATURE_POINT_DATA:
                fratureInt = ProtocolBase.STATUS_POINT_DATA;
                break;
        }
        return fratureInt;
    }
}
